
`include "common_header.verilog"

//  *************************************************************************
//  File : pfc_xmit_ctl_64
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized reproduction or use is expressly prohibited.
//  Copyright (c) 2014 MorethanIP
//  Muenchner Strasse 199, 85757 Karlsfeld, Germany
//  info@morethanip.com
//  http://www.morethanip.com
//  *************************************************************************
//  Designed by : Daniel Koehler, Thomas Schrobenhauser
//  info@morethanip.com
//  *************************************************************************
//  Description : Pause/PFC Frame control and content generator for transmit.
//  Version     : $Id: pfc_xmit_ctl_64.v,v 1.2 2014/07/15 16:28:36 ts Exp $
//  *************************************************************************

module pfc_xmit_ctl_64 (
   reset_txclk,
   xgmii_txclk,
   xgmii_txclk_ena,
   tx_enable,
   qtime_t,
   qtime_t_run,
   pause_gen,
   pause_gen_data,
   pause_gen_wcnt,
   pfc_mode,
   mac_addr,
   cl_pause_quanta,
   cl_pause_thresh,
   tx_pfc_xoff,
   tx_pfc_ack,
   tsv_stat_pfc);

input   reset_txclk;                    //  Active High reset for xgmii_txclk domain
input   xgmii_txclk;                    //  XGMII transmit clock
input   xgmii_txclk_ena;                //  XGMII Transmit Clock tx_enable.
input   tx_enable;                      //  tx datapath enable
input   qtime_t;                        //  pulse when pause time of 1 expired (512 bits) (txclk)
output  qtime_t_run;                    //  timer is enabled, should generate pulses
output  pause_gen;                      //  Pause frame generate request
output  [63:0] pause_gen_data;          //  data from pause generator
input   [2:0] pause_gen_wcnt;           //  word counter for pause generator content generation
input   pfc_mode;                       //  PFC mode(1) or Link Pause mode (0)
input   [47:0] mac_addr;                //  Device Ethernet MAC address
input   [127:0] cl_pause_quanta;        //  Class 0..7 PFC Quanta value
input   [127:0] cl_pause_thresh;        //  Threshold for Class 0..7 Refresh PFC Frame
input   [7:0] tx_pfc_xoff;              //  Transmit Flow Control Generate
output  tx_pfc_ack;                     //  Transmit Flow Control Acknowledge (Generate was sampled)
output  [7:0] tsv_stat_pfc;             //  the class enable of latest generated frame

wire    qtime_t_run;
reg     pause_gen;
reg     [63:0] pause_gen_data;
reg     tx_pfc_ack;
wire    [7:0] tsv_stat_pfc;

// TYPE stm_typ:
parameter STM_TYP_IDLE      = 2'd 0;
parameter STM_TYP_GEN_START = 2'd 1;
parameter STM_TYP_GEN_WAIT  = 2'd 2;
parameter STM_TYP_GEN_DONE  = 2'd 3;

reg     [1:0] state;
reg     [1:0] nextstate;

//  per class timer wirings
wire    [15:0] quanta_o_a [7:0];
wire    [7:0] cena_o_a;
wire    [7:0] pause_req_a;
wire    [7:0] qtime_run_a;
wire    pause_busy;
wire    pause_req;
reg     pfc_enable;     //  enable PFC class based pause
wire    [3:0] xwcnt;    //  word counter for pause generator content generation
reg     [7:0] pfc_cena; //  pfc class enable bits

//  indicate to timer that it should operate
assign qtime_t_run = qtime_run_a == 8'h 00 ? 1'b 0 : 1'b 1;

//  input controls
//  --------------
always @(posedge reset_txclk or posedge xgmii_txclk)
   begin : process_1
   if (reset_txclk == 1'b 1)
      begin
      pfc_enable <= 1'b 0;
      end
   else
      begin
      if (tx_enable == 1'b 1 & pfc_mode == 1'b 1)
         begin
         pfc_enable <= 1'b 1;
         end
      else
         begin
         pfc_enable <= 1'b 0;
         end
      end
   end

//  Generator STM
//  -------------
always @(posedge reset_txclk or posedge xgmii_txclk)
   begin : pstmr
   if (reset_txclk == 1'b 1)
      begin
      state <= STM_TYP_IDLE;
      end
   else
      begin
      if (tx_enable == 1'b 0)
         begin
         state <= STM_TYP_IDLE;
         end
      //  CLOCK ENABLE
      else if (xgmii_txclk_ena == 1'b 1 )
         begin
         state <= nextstate;
         end
      end
   end

assign pause_req = |pause_req_a;        // any request

//  handshake with the per class frame trigger controls indicating when
//  the pause frame is actually transmitted
assign pause_busy = pause_gen_wcnt != 3'b 000 ? 1'b 1 : 1'b 0;

always @(state or tx_enable or pause_req or pause_gen_wcnt)
   begin : pstm
   case (state)
   STM_TYP_IDLE:
      begin
      if (tx_enable == 1'b 1 & pause_req == 1'b 1)
         begin
         nextstate = STM_TYP_GEN_START;
         end
      else
         begin
         nextstate = STM_TYP_IDLE;
         end
      end

   STM_TYP_GEN_START:
      begin
      //  trigger pause frame to MAC and wait until it starts producing the frame
      if (pause_gen_wcnt != 3'b 000)
         begin
         //  ok, TX is producing the pause frame now
         nextstate = STM_TYP_GEN_WAIT;
         end
      else
         begin
         nextstate = STM_TYP_GEN_START;
         end
      end

   STM_TYP_GEN_WAIT:
      begin
      //  wait until pause frame has been transmitted
      //  and TX STM is back into idle.
      if (pause_gen_wcnt == 3'b 000)
         begin
         nextstate = STM_TYP_GEN_DONE;
         end
      else
         begin
         nextstate = STM_TYP_GEN_WAIT;
         end
      end

   STM_TYP_GEN_DONE:
      begin
      nextstate = STM_TYP_IDLE;
      end

   default:
      begin
      nextstate = STM_TYP_IDLE;
      end
   endcase
   end

//  controls
//  --------
always @(posedge reset_txclk or posedge xgmii_txclk)
   begin : pctl
   if (reset_txclk == 1'b 1)
      begin
      tx_pfc_ack <= 1'b 0;
      end
   else
      begin
      //  CLOCK ENABLE
      if (xgmii_txclk_ena == 1'b 1)
         begin
         //  indicate when current info has been sampled and is now being transmitted
         if (state == STM_TYP_GEN_START & nextstate == STM_TYP_GEN_WAIT)
            begin
            tx_pfc_ack <= 1'b 1;
            end
         else
            begin
            tx_pfc_ack <= 1'b 0;
            end
         end
      end
   end

//  create pause frame content
//  --------------------------

assign xwcnt = {pfc_enable, pause_gen_wcnt};

always @(posedge reset_txclk or posedge xgmii_txclk)
   begin : pgen
   if (reset_txclk == 1'b 1)
      begin
      pause_gen <= 1'b 0;
      pause_gen_data <= {64{1'b 0}};
      pfc_cena <= {8{1'b 0}};
      end
   else
      begin
      //  CLOCK ENABLE
      if (xgmii_txclk_ena == 1'b 1)
         begin
         //  indicate pause transmit request to MAC
         if (state == STM_TYP_GEN_START)
            begin
            pause_gen <= 1'b 1;
            end
         else
            begin
            pause_gen <= 1'b 0;
            end

         //  sample class enable bits for all classes that request a pause frame transmission
         if (state == STM_TYP_GEN_WAIT & pause_gen_wcnt == 3'b 011)
            begin
            if (pfc_enable == 1'b 0)
               begin
               pfc_cena <= {8{1'b 0}};
               end
            else
               begin
               pfc_cena <= cena_o_a;
               end
            end

         //  create contents (without preamble)
         //  ----------------------------------
         case (xwcnt)

         //  link pause frame
         //  ----------------
         4'b 0000:
            begin
            pause_gen_data <= {mac_addr[15:0], 48'h 010000c28001};
            end
         4'b 0001:
            begin
            pause_gen_data <= {32'h 01000888, mac_addr[47:16]};   //  88-08 00-01
            end
         4'b 0010:
            begin
            pause_gen_data <= {48'h 000000000000, quanta_o_a[0]};   //  is in network byte order already!
            end

         //  PFC pause frame
         //  ---------------
         4'b 1000:
            begin
            pause_gen_data <= {mac_addr[15:0], 48'h 010000c28001};
            end
         4'b 1001:
            begin
            pause_gen_data <= {32'h 01010888, mac_addr[47:16]};   //  88-08 01-01
            end
         4'b 1010:
            begin
            pause_gen_data <= {quanta_o_a[2], quanta_o_a[1], quanta_o_a[0], cena_o_a, 8'h 00};
            end
         4'b 1011:
            begin
            pause_gen_data <= {quanta_o_a[6], quanta_o_a[5], quanta_o_a[4], quanta_o_a[3]};
            end
         4'b 1100:
            begin
            pause_gen_data <= {48'h 000000000000, quanta_o_a[7]};
            end

         default:
            begin
            pause_gen_data <= 64'h 0000000000000000;
            end
         endcase
         end
      end
   end

assign tsv_stat_pfc = pfc_cena; //  latched content for statistics

//  ----------------------
//  Pause trigger control.
//  ----------------------

//  Class0 is used for PFC as well as Link Pause mode
//  -------------------------------------------------
pause_tx_trigctl U_C0 (
          .reset_txclk(reset_txclk),
          .xgmii_txclk(xgmii_txclk),
          .xgmii_txclk_ena(xgmii_txclk_ena),
          .qtime(qtime_t),
          .p_enable(tx_enable),                 //  always enabled if TX datapath is enabled
          .tx_pfc_xoff(tx_pfc_xoff[0]),         //  used in PFC and link pause mode
          .pause_quanta(cl_pause_quanta[15:0]),
          .pause_thresh(cl_pause_thresh[15:0]),
          .pause_quanta_o(quanta_o_a[0]),       //  quanta to use for transmission
          .pause_cena_o(cena_o_a[0]),           //  class enable to use for transmission
          .pause_req(pause_req_a[0]),
          .qtime_run(qtime_run_a[0]),
          .pause_busy(pause_busy));

//  per class pause trigger control
//  -------------------------------

pause_tx_trigctl U_C_G_1 (
          .reset_txclk(reset_txclk),
          .xgmii_txclk(xgmii_txclk),
          .xgmii_txclk_ena(xgmii_txclk_ena),
          .qtime(qtime_t),
          .p_enable(pfc_enable),
          .tx_pfc_xoff(tx_pfc_xoff[1]),
          .pause_quanta(cl_pause_quanta[16 * 1 + 15:16 * 1]),
          .pause_thresh(cl_pause_thresh[16 * 1 + 15:16 * 1]),
          .pause_quanta_o(quanta_o_a[1]),
          .pause_cena_o(cena_o_a[1]),
          .pause_req(pause_req_a[1]),
          .qtime_run(qtime_run_a[1]),
          .pause_busy(pause_busy));

pause_tx_trigctl U_C_G_2 (
          .reset_txclk(reset_txclk),
          .xgmii_txclk(xgmii_txclk),
          .xgmii_txclk_ena(xgmii_txclk_ena),
          .qtime(qtime_t),
          .p_enable(pfc_enable),
          .tx_pfc_xoff(tx_pfc_xoff[2]),
          .pause_quanta(cl_pause_quanta[16 * 2 + 15:16 * 2]),
          .pause_thresh(cl_pause_thresh[16 * 2 + 15:16 * 2]),
          .pause_quanta_o(quanta_o_a[2]),
          .pause_cena_o(cena_o_a[2]),
          .pause_req(pause_req_a[2]),
          .qtime_run(qtime_run_a[2]),
          .pause_busy(pause_busy));

pause_tx_trigctl U_C_G_3 (
          .reset_txclk(reset_txclk),
          .xgmii_txclk(xgmii_txclk),
          .xgmii_txclk_ena(xgmii_txclk_ena),
          .qtime(qtime_t),
          .p_enable(pfc_enable),
          .tx_pfc_xoff(tx_pfc_xoff[3]),
          .pause_quanta(cl_pause_quanta[16 * 3 + 15:16 * 3]),
          .pause_thresh(cl_pause_thresh[16 * 3 + 15:16 * 3]),
          .pause_quanta_o(quanta_o_a[3]),
          .pause_cena_o(cena_o_a[3]),
          .pause_req(pause_req_a[3]),
          .qtime_run(qtime_run_a[3]),
          .pause_busy(pause_busy));

pause_tx_trigctl U_C_G_4 (
          .reset_txclk(reset_txclk),
          .xgmii_txclk(xgmii_txclk),
          .xgmii_txclk_ena(xgmii_txclk_ena),
          .qtime(qtime_t),
          .p_enable(pfc_enable),
          .tx_pfc_xoff(tx_pfc_xoff[4]),
          .pause_quanta(cl_pause_quanta[16 * 4 + 15:16 * 4]),
          .pause_thresh(cl_pause_thresh[16 * 4 + 15:16 * 4]),
          .pause_quanta_o(quanta_o_a[4]),
          .pause_cena_o(cena_o_a[4]),
          .pause_req(pause_req_a[4]),
          .qtime_run(qtime_run_a[4]),
          .pause_busy(pause_busy));

pause_tx_trigctl U_C_G_5 (
          .reset_txclk(reset_txclk),
          .xgmii_txclk(xgmii_txclk),
          .xgmii_txclk_ena(xgmii_txclk_ena),
          .qtime(qtime_t),
          .p_enable(pfc_enable),
          .tx_pfc_xoff(tx_pfc_xoff[5]),
          .pause_quanta(cl_pause_quanta[16 * 5 + 15:16 * 5]),
          .pause_thresh(cl_pause_thresh[16 * 5 + 15:16 * 5]),
          .pause_quanta_o(quanta_o_a[5]),
          .pause_cena_o(cena_o_a[5]),
          .pause_req(pause_req_a[5]),
          .qtime_run(qtime_run_a[5]),
          .pause_busy(pause_busy));

pause_tx_trigctl U_C_G_6 (
          .reset_txclk(reset_txclk),
          .xgmii_txclk(xgmii_txclk),
          .xgmii_txclk_ena(xgmii_txclk_ena),
          .qtime(qtime_t),
          .p_enable(pfc_enable),
          .tx_pfc_xoff(tx_pfc_xoff[6]),
          .pause_quanta(cl_pause_quanta[16 * 6 + 15:16 * 6]),
          .pause_thresh(cl_pause_thresh[16 * 6 + 15:16 * 6]),
          .pause_quanta_o(quanta_o_a[6]),
          .pause_cena_o(cena_o_a[6]),
          .pause_req(pause_req_a[6]),
          .qtime_run(qtime_run_a[6]),
          .pause_busy(pause_busy));

pause_tx_trigctl U_C_G_7 (
          .reset_txclk(reset_txclk),
          .xgmii_txclk(xgmii_txclk),
          .xgmii_txclk_ena(xgmii_txclk_ena),
          .qtime(qtime_t),
          .p_enable(pfc_enable),
          .tx_pfc_xoff(tx_pfc_xoff[7]),
          .pause_quanta(cl_pause_quanta[16 * 7 + 15:16 * 7]),
          .pause_thresh(cl_pause_thresh[16 * 7 + 15:16 * 7]),
          .pause_quanta_o(quanta_o_a[7]),
          .pause_cena_o(cena_o_a[7]),
          .pause_req(pause_req_a[7]),
          .qtime_run(qtime_run_a[7]),
          .pause_busy(pause_busy));

endmodule // module pfc_xmit_ctl_64
